/******************** (C) COPYRIGHT 2018 merafour ********************
* Author             : 冷月追风@merafour.blog.163.com
* Version            : V1.0.0
* Date               : 8/1/2019
* Description        : IPMB协议与数据处理.
********************************************************************************
* merafour.blog.163.com
* merafour@163.com
* github.com/Merafour
*******************************************************************************/
#ifndef IPMC_PROTOCOL_H
#define IPMC_PROTOCOL_H
#include <stdint.h>
#include <string.h>
#include "menu/qitranslator.h"
#include "cJSON/cJSON.h"

#define LIST_IPMC_ADDR_MAX        64

//#pragma anon_unions
struct ipmi_protocol{
    uint8_t rsSA;
    //uint8_t NetFn;
    //union {
        uint8_t NetFn;
        uint8_t rsLun;
    //};
    uint8_t check_sum1;
    uint8_t rqSA;
    //union {
        uint8_t rqSeq;
        uint8_t rqLun;
    //};
    uint8_t cmd;
    uint8_t code;
    uint8_t data[16];
    uint8_t lenght;
    uint8_t check_sum2;
};
struct vpx_ipmb_addr{
    uint8_t solt_id;  // VPX Slot ID
    uint8_t gap:1;    // GAP*
    uint8_t ga4:1;    // GA4*
    uint8_t ga3:1;    // GA3*
    uint8_t ga2:1;    // GA2*
    uint8_t ga1:1;    // GA1*
    uint8_t ga0:1;    // GA1*
    uint8_t recv:2;   // GA0*
    uint8_t hard;     // Hardware Address
    uint8_t ipmb;     // IPMB Address
};
//struct ipmc_data_unit
//{
//    float value;  // 数据
//    const uint8_t id;   // ID号
//};
// IPMC数据
struct ipmc_data {
    uint8_t solt_id;
    uint8_t dev_type;
    uint8_t status;     // 自检状态（CPU→IPMC）
    uint8_t code;       // 对齐
//    struct ipmc_data_unit temperature;  // 温度（CPU→IPMC）, 单位为℃，
//    struct ipmc_data_unit imax[];
    struct {
        float value;        // 数据
        uint8_t id;         // ID号
        uint8_t code;
        uint8_t recv[2];    // 对齐
    }item[20];
};
#if 0
// CPU数据
struct ipmb_cpu_data {
    struct ipmc_data _ipmc[32];
    uint8_t list_ipmc_addr[LIST_IPMC_ADDR_MAX];
};
#endif
struct ipmc_id_item{
    const uint8_t id;
    const char info[31];
};
struct ipmc_device_type{
    const uint8_t value;
    const char info[31];
};

class ipmc_protocolObj
{
public:
    //ipmc_protocolObj(ipmb_cpu_data &_data);
    ipmc_protocolObj();

    // CPU→IPMC
    void pack_send_ipmc(struct ipmi_protocol *pack, const uint8_t slave_addr, const uint8_t NetFn, \
                        const uint8_t cmd, const uint8_t data);
    uint8_t encode_send_ipmc(struct ipmi_protocol *pack, uint8_t buffer[], const uint8_t lenght);
    int decode_send_ipmc(struct ipmi_protocol *pack, const uint8_t own_addr, const uint8_t buffer[], const uint8_t lenght);
    // IPMC →CPU
    void pack_send_cpu(struct ipmi_protocol *pack, const uint8_t slave_addr, const uint8_t NetFn, \
                      const uint8_t cmd, const uint8_t rqSeq, const uint8_t code, \
                      const uint8_t data1, const uint8_t data2, const uint8_t data3);
    uint8_t encode_send_cpu(struct ipmi_protocol *pack, uint8_t buffer[], const uint8_t lenght);
    int decode_send_cpu(struct ipmi_protocol *pack, const uint8_t own_addr, const uint8_t buffer[], const uint8_t lenght);
    int decode_item(const struct ipmi_protocol *pack);

    static uint8_t get_slave_addr(const uint8_t vpx_solt_id)
    {
        uint8_t index=0;
        uint8_t _size=sizeof (vpx_ipmb_addr_table)/sizeof (vpx_ipmb_addr_table[0]);
        for(index=0; index<_size; index++)
        {
            if(vpx_solt_id==vpx_ipmb_addr_table[index].solt_id)
            {
                return vpx_ipmb_addr_table[index].ipmb;
            }
        }
        return vpx_ipmb_addr_table[0].ipmb;
    }
    static uint8_t get_solt_id(const uint8_t _slave_addr)
    {
        uint8_t index=0;
        uint8_t _size=sizeof (vpx_ipmb_addr_table)/sizeof (vpx_ipmb_addr_table[0]);
        for(index=0; index<_size; index++)
        {
            if(_slave_addr==vpx_ipmb_addr_table[index].solt_id)
            {
                return vpx_ipmb_addr_table[index].solt_id;
            }
        }
        return vpx_ipmb_addr_table[0].solt_id;
    }
    static const char* get_device_type(const uint8_t _value)
    {
        uint8_t index=0;
        uint8_t _size=sizeof (ipmc_device_table)/sizeof (ipmc_device_table[0]);
        for(index=0; index<_size; index++)
        {
            if(_value==ipmc_device_table[index].value)
            {
                return ipmc_device_table[index].info;
            }
        }
        return ipmc_device_table[0].info;
    }
    static const char* get_item_info(const uint8_t item_id)
    {
        uint8_t index=0;
        uint8_t _size = sizeof (ipmc_id_table)/sizeof (ipmc_id_table[0]);
        for (index=0; index<_size; index++)
        {
            if(item_id==ipmc_id_table[index].id)
            {
                return ipmc_id_table[index].info;
            }
        }
        return ipmc_id_table[19].info;
    }
    static uint8_t search_item_id(const char *_info)
    {
        uint8_t index=0;
        uint8_t _size = sizeof (ipmc_id_table)/sizeof (ipmc_id_table[0]);
        for (index=0; index<_size; index++)
        {
            if(0==strcmp(ipmc_id_table[index].info, _info))
            {
                return index;
            }
        }
        return _size-1;
    }
    uint8_t search_item_id(const uint8_t item_id)
    {
        uint8_t index=0;
        uint8_t _size = sizeof (ipmc_id_table)/sizeof (ipmc_id_table[0]);
        for (index=0; index<_size; index++)
        {
            if(item_id==ipmc_id_table[index].id)
            {
                return index;
            }
        }
        return _size-1;;
    }
    uint8_t get_ipmb_addr(void)
    {
        return  ipmb_addr;
    }
    int get_ipmc_data_faddr(struct ipmc_data &_data, const uint8_t slave_addr)
    {
        uint8_t _solt_id=0;
        _solt_id = get_solt_id(slave_addr);
        memcpy(&_data, &_ipmc_data[_solt_id], sizeof (struct ipmc_data));
        return 0;
    }
    int list_insert_addr(const uint8_t _addr);
    int list_clear_addr(void);
    void list_set_row(const uint8_t _row)
    {
        if(currentRow<list_get_size()) currentRow = _row;
    }
    uint8_t list_get_addr_max(void)
    {
        return list_ipmc_addr_max;
    }
    uint8_t list_get_addr(void)
    {
        return list_ipmc_addr[currentRow];
    }
    uint8_t list_get_addr(uint8_t _row)
    {
        if(currentRow<list_get_size()) return list_ipmc_addr[_row];
        return 0x00;
    }
    uint8_t list_get_size(void)
    {
#if 0
        uint8_t _size = sizeof (list_ipmc_addr)/sizeof (list_ipmc_addr[0]);
        return  _size;

#else
        return LIST_IPMC_ADDR_MAX;
#endif
    }

    // IPMB信息描述,用于在界面上显示
    static const struct ipmc_id_item ipmc_id_table[20];
    static const struct ipmc_device_type ipmc_device_table[8];

private:
    // IPMB地址表
    static const struct vpx_ipmb_addr vpx_ipmb_addr_table[32];
    uint8_t cmd_rqSeq;
    uint8_t ipmb_addr;
    uint8_t rev[4];   // 对齐
    //struct ipmb_cpu_data &_cpu_data;
    // IPMB数据
    struct ipmc_data _ipmc_data[32];
    uint8_t list_ipmc_addr[LIST_IPMC_ADDR_MAX];  // 地址列表
    uint8_t currentRow;
    uint8_t list_ipmc_addr_max;
    //struct ipmc_data * const _ipmc_data;
    uint8_t calc_checksum(const uint8_t *data, const uint8_t length)
    {
        uint8_t Checksum = 0;
        uint8_t i = 0;
        uint8_t data_length = 0;

        data_length = length;

        if ((nullptr == data) || (0 == length))
        {
            return Checksum;
        }
        else
        {
            for (i = 0; i < data_length; i++)
            {
                Checksum = (Checksum + data[i]) % 256;
            }
            return (-Checksum);
        }
    }
    uint8_t calc_checksum_2byte(const uint8_t byte1, const uint8_t byte2)
    {
        uint8_t data[2];
        data[0] = byte1;
        data[1] = byte2;
        return calc_checksum(data, 2);
    }
    uint8_t search_solt_id(const uint8_t _ipmb_addr)
    {
        uint8_t index=0;
        for (index=0; index<32; index++)
        {
            if(_ipmb_addr==vpx_ipmb_addr_table[index].ipmb)
            {
                return vpx_ipmb_addr_table[index].solt_id;
            }
        }
        return vpx_ipmb_addr_table[0].solt_id;
    }

    int create_json(void);
};

#endif // IPMC_PROTOCOL_H
